<template>
    <div ref="container" class="waterfall">
        <div ref="scroll" :class="`scroll ${ state.layouting ? 'hidden' : '' }`">
            <slot />
        </div>
    </div>
</template>

<script lang="ts">
export default { name: 'Waterfall' }
</script>

<script lang="ts" setup>
import { getCurrentInstance, ComponentInternalInstance, reactive, watch, computed, ref, nextTick, onMounted, useAttrs, useSlots } from 'vue'

const slots = useSlots()
// const attrs = useAttrs()
// const emits = defineEmits()
const { proxy } = getCurrentInstance() as ComponentInternalInstance

const props = defineProps({
    col: {
        type: Number,
        default: 2,
    },
    linePadding: { // 行边距
        type: Number,
        default: 10,
    },
    colPadding: { // 列边距
        type: Number,
        default: 10,
    },
})

const container = ref()
const scroll = ref()

// 初始化 topList leftList
const reset = () => {
    state.topList = []
    const blockWidth = (container.value.clientWidth - props.col * props.colPadding) / props.col
    state.leftList = [props.colPadding]
    for (let i = 0; i < props.col; i++) {
        state.topList.push(props.linePadding)
        if (i !== 0) state.leftList.push(state.leftList.slice(-1)[0] + props.colPadding + blockWidth)
    }
}

const doLayout = () => {
    state.layouting = true
    reset()

    const childs:HTMLElement[] = []
    for (let i = 0; i < scroll.value.children.length; i++) {
        childs.push(scroll.value.children[i])
    }
    childs.sort((a, b) => b.offsetHeight - a.offsetHeight)
    for (let i = 0; i < childs.length; i++) {
        const item = childs[i]
        item.style.position = 'absolute'

        let posIndex = 0
        if (i < props.col) { // 第一行按照顺序布局
            posIndex = i % props.col
        } else { // 从第二行起，找最短那列插入
            let min = state.topList[0]
            state.topList.forEach((item, index) => {
                if (item < min) {
                    min = item
                    posIndex = index
                }
            })
        }
        item.style.top = `${state.topList[posIndex]}px`
        state.topList[posIndex] = state.topList[posIndex] + item.offsetHeight + props.linePadding

        item.style.left = `${state.leftList[posIndex]}px`
    }

    state.layouting = false
}

const childDoms = computed(() => (slots.default ? slots.default() : []))

watch([
    () => childDoms.value,
    () => props.col,
    () => props.linePadding,
    () => props.colPadding,
], () => {
    doLayout()
})
interface State{
    layouting: boolean,
    topList: number[],
    leftList: number[],
}
const state:State = reactive({
    layouting: false, // 是否在布局中，布局时display: none；
    topList: [], // dom top列表
    leftList: [], // dom left列表
})

onMounted(() => {
    doLayout()
})

defineExpose({
    doLayout,
})
</script>
<style lang="scss" scoped>
.waterfall {
    padding: 10px;

    // height: 500px;
    overflow: auto;

    .scroll {
        // height: 800px;
        &.hidden {
            display: none !important;
        }

        position: relative;
    }
}
</style>
